﻿using OpenTK;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Windows.Forms;
using LightCAD.MathLib;


namespace LightCAD.Three
{

    //public class TransformControls : Object3D
    //{
    //    private struct Unit
    //    {
    //        public Vector3 X;
    //        public Vector3 Y;
    //        public Vector3 Z;

    //        public new Vector3 this[string axis]
    //        {
    //            get
    //            {
    //                switch (axis)
    //                {
    //                    case "X":
    //                        return this.X;
    //                    case "Y":
    //                        return this.Y;
    //                    case "Z":
    //                        return this.Z;
    //                }
    //                return null;
    //            }
    //            set
    //            {
    //                switch (axis)
    //                {
    //                    case "X":
    //                        this.X = value;
    //                        break;
    //                    case "Y":
    //                        this.Y = value;
    //                        break;
    //                    case "Z":
    //                        this.Z = value;
    //                        break;
    //                }
    //            }
    //        }
    //    }

    //    private struct Pointer
    //    {
    //        public double x;
    //        public double y;
    //        public MouseButtons button;
    //        public Vector2 ToV2()
    //        {
    //            return new Vector2(x, y);
    //        }
    //    }

    //    private static Euler _tempEuler = new Euler();
    //    private static Vector3 _alignVector = new Vector3(0, 1, 0);
    //    private static Vector3 _zeroVector = new Vector3(0, 0, 0);
    //    private static Matrix4 _lookAtMatrix = new Matrix4();
    //    private static Quaternion _tempQuaternion2 = new Quaternion();
    //    private static Quaternion _identityQuaternion = new Quaternion();
    //    private static Vector3 _dirVector = new Vector3();
    //    private static Matrix4 _tempMatrix = new Matrix4();

    //    private static Vector3 _unitX = new Vector3(1, 0, 0);
    //    private static Vector3 _unitY = new Vector3(0, 1, 0);
    //    private static Vector3 _unitZ = new Vector3(0, 0, 1);

    //    private static Vector3 _v1 = new Vector3();
    //    private static Vector3 _v2 = new Vector3();
    //    private static Vector3 _v3 = new Vector3();

    //    public class TransformControlsGizmo : Object3D
    //    {
    //        private struct ControlInfo
    //        {
    //            public Object3D obj3d;
    //            public double[] position;
    //            public double[] rotation;
    //            public double[] scale;
    //            public string tag;
    //            public ControlInfo(Object3D obj3d, double[] position = null, double[] rotation = null, double[] scale = null, string tag = null)
    //            {
    //                this.obj3d = obj3d;
    //                this.position = position;
    //                this.rotation = rotation;
    //                this.scale = scale;
    //                this.tag = tag;
    //            }
    //        }

    //        public JsObj<string, Object3D> gizmo;
    //        public JsObj<string, Object3D> picker;
    //        public JsObj<string, Object3D> helper;
    //        public string mode;
    //        public string space;
    //        public Quaternion worldQuaternion;
    //        public Quaternion worldQuaternionStart;
    //        public Vector3 worldPosition;
    //        public Vector3 worldPositionStart;
    //        public Camera camera;
    //        public Vector3 cameraPosition;
    //        public double size;
    //        public string axis;
    //        public Vector3 eye;
    //        public Vector3 rotationAxis;
    //        public bool dragging;

    //        public bool showX;
    //        public bool showY;
    //        public bool showZ;

    //        public bool enabled;
    //        //public Object3D obj;
    //        //public object translationSnap;
    //        //public object rotationSnap;
    //        //public object scaleSnap;

    //        public TransformControlsGizmo() : base()
    //        {
    //            this.type = "TransformControlsGizmo";

    //            // shared materials

    //            var gizmoMaterial = new MeshBasicMaterial
    //            {
    //                depthTest = false,
    //                depthWrite = false,
    //                fog = false,
    //                toneMapped = false,
    //                transparent = true
    //            };

    //            var gizmoLineMaterial = new LineBasicMaterial
    //            {
    //                depthTest = false,
    //                depthWrite = false,
    //                fog = false,
    //                toneMapped = false,
    //                transparent = true
    //            };

    //            // Make unique material for each axis/color

    //            var matInvisible = gizmoMaterial.clone();
    //            matInvisible.opacity = 0.15;

    //            var matHelper = gizmoLineMaterial.clone();
    //            matHelper.opacity = 0.5;

    //            var matRed = gizmoMaterial.clone();
    //            matRed.color.SetHex(0xff0000);

    //            var matGreen = gizmoMaterial.clone();
    //            matGreen.color.SetHex(0x00ff00);

    //            var matBlue = gizmoMaterial.clone();
    //            matBlue.color.SetHex(0x0000ff);

    //            var matRedTransparent = gizmoMaterial.clone();
    //            matRedTransparent.color.SetHex(0xff0000);
    //            matRedTransparent.opacity = 0.5;

    //            var matGreenTransparent = gizmoMaterial.clone();
    //            matGreenTransparent.color.SetHex(0x00ff00);
    //            matGreenTransparent.opacity = 0.5;

    //            var matBlueTransparent = gizmoMaterial.clone();
    //            matBlueTransparent.color.SetHex(0x0000ff);
    //            matBlueTransparent.opacity = 0.5;

    //            var matWhiteTransparent = gizmoMaterial.clone();
    //            matWhiteTransparent.opacity = 0.25;

    //            var matYellowTransparent = gizmoMaterial.clone();
    //            matYellowTransparent.color.SetHex(0xffff00);
    //            matYellowTransparent.opacity = 0.25;

    //            var matYellow = gizmoMaterial.clone();
    //            matYellow.color.SetHex(0xffff00);

    //            var matGray = gizmoMaterial.clone();
    //            matGray.color.SetHex(0x787878);

    //            // reusable geometry

    //            var arrowGeometry = new CylinderGeometry(0, 0.04, 0.1, 12);
    //            arrowGeometry.translate(0, 0.05, 0);

    //            var scaleHandleGeometry = new BoxGeometry(0.08, 0.08, 0.08);
    //            scaleHandleGeometry.translate(0, 0.04, 0);

    //            var lineGeometry = new BufferGeometry();
    //            lineGeometry.setAttribute("position", new Float32BufferAttribute(new double[] { 0, 0, 0, 1, 0, 0 }, 3));

    //            var lineGeometry2 = new CylinderGeometry(0.0075, 0.0075, 0.5, 3);
    //            lineGeometry2.translate(0, 0.25, 0);

    //            TorusGeometry CircleGeometry(double radius, double arc)
    //            {
    //                var geometry = new TorusGeometry(radius, 0.0075, 3, 64, arc * Math.PI * 2);
    //                geometry.rotateY(Math.PI / 2);
    //                geometry.rotateX(Math.PI / 2);
    //                return geometry;
    //            }

    //            // Special geometry for transform helper. If scaled with position vector it spans from [0,0,0] to position
    //            BufferGeometry TranslateHelperGeometry()
    //            {
    //                var geometry = new BufferGeometry();
    //                geometry.setAttribute("position", new Float32BufferAttribute(new double[] { 0, 0, 0, 1, 1, 1 }, 3));
    //                return geometry;
    //            }

    //            // Gizmo definitions - custom hierarchy definitions for setupGizmo() function

    //            var gizmoTranslate = new JsObj<string, ListEx<ControlInfo>>
    //                {
    //                    { "X", new ListEx<ControlInfo>
    //                        {
    //                            new ControlInfo(new Mesh(arrowGeometry, matRed ), new double[]{ 0.5, 0, 0 }, new double[]{ 0, 0, - Math.PI / 2 }),
    //                            new ControlInfo(new Mesh(arrowGeometry, matRed ), new double[]{ -0.5, 0, 0  }, new double[]{ 0, 0, Math.PI / 2 }),
    //                            new ControlInfo(new Mesh(lineGeometry2, matRed ), new double[]{ 0, 0, 0   }, new double[]{ 0, 0, - Math.PI / 2 }),
    //                        }
    //                    },
    //                    { "Y", new ListEx<ControlInfo>
    //                        {
    //                            new ControlInfo(new Mesh(arrowGeometry, matGreen), new double[]{ 0, 0.5, 0 }, null),
    //                            new ControlInfo(new Mesh(arrowGeometry, matGreen), new double[]{ 0, -0.5, 0 }, new double[]{Math.PI, 0, 0 }),
    //                            new ControlInfo(new Mesh(lineGeometry2, matGreen), null, null)
    //                        }
    //                    },
    //                    {"Z", new ListEx<ControlInfo>
    //                        {
    //                            new ControlInfo(new Mesh(arrowGeometry, matBlue), new double[]{ 0, 0, 0.5 }, new double[]{ Math.PI / 2, 0, 0 } ),
    //                            new ControlInfo(new Mesh(arrowGeometry, matBlue), new double[]{ 0, 0, -0.5 }, new double[]{ -Math.PI / 2, 0, 0 } ),
    //                            new ControlInfo(new Mesh(lineGeometry2, matBlue), null, new double[]{ Math.PI / 2, 0, 0 } )
    //                        }
    //                    },
    //                    { "XYZ", new ListEx<ControlInfo>
    //                        {
    //                            new ControlInfo( new Mesh(new OctahedronGeometry(0.1, 0), matWhiteTransparent.clone()), new double[]{ 0, 0, 0 }, null )
    //                        }
    //                    },
    //                    { "XY", new ListEx<ControlInfo>
    //                        {
    //                            new ControlInfo(new Mesh(new BoxGeometry(0.15, 0.15, 0.01), matBlueTransparent.clone()), new double[]{ 0.15, 0.15, 0 }, null )
    //                        }
    //                    },
    //                    { "YZ", new ListEx<ControlInfo>
    //                        {
    //                            new ControlInfo(new Mesh(new BoxGeometry(0.15, 0.15, 0.01), matRedTransparent.clone()), new double[]{ 0, 0.15, 0.15 }, new double[]{ 0, Math.PI / 2, 0 } )
    //                        }
    //                    },
    //                    { "XZ",new ListEx<ControlInfo>
    //                        {
    //                            new ControlInfo( new Mesh(new BoxGeometry(0.15, 0.15, 0.01), matGreenTransparent.clone()), new double[]{ 0.15, 0, 0.15 }, new double[]{ -Math.PI / 2, 0, 0 } )
    //                        }
    //                    }
    //                };

    //            var pickerTranslate = new JsObj<string, ListEx<ControlInfo>>
    //                {
    //                    { "X", new ListEx<ControlInfo>
    //                        {
    //                            new ControlInfo( new Mesh( new CylinderGeometry( 0.2, 0, 0.6, 4 ), matInvisible ), new double[]{ 0.3, 0, 0 }, new double[]{ 0, 0, - Math.PI / 2 }),
    //                            new ControlInfo( new Mesh( new CylinderGeometry( 0.2, 0, 0.6, 4 ), matInvisible ), new double[]{ - 0.3, 0, 0 }, new double[]{ 0, 0, Math.PI / 2 })
    //                        }
    //                    },
    //                    { "Y", new ListEx<ControlInfo>
    //                        {
    //                            new ControlInfo( new Mesh( new CylinderGeometry( 0.2, 0, 0.6, 4 ), matInvisible ), new double[]{ 0, 0.3, 0 }, null),
    //                            new ControlInfo( new Mesh( new CylinderGeometry( 0.2, 0, 0.6, 4 ), matInvisible ), new double[]{ 0, - 0.3, 0 }, new double[]{ 0, 0, Math.PI })
    //                        }
    //                    },
    //                    { "Z", new ListEx<ControlInfo>
    //                        {
    //                            new ControlInfo( new Mesh( new CylinderGeometry( 0.2, 0, 0.6, 4 ), matInvisible ), new double[]{ 0, 0, 0.3 }, new double[]{Math.PI / 2, 0, 0 }),
    //                            new ControlInfo( new Mesh( new CylinderGeometry( 0.2, 0, 0.6, 4 ), matInvisible ), new double[]{ 0, 0, - 0.3 }, new double[]{ - Math.PI / 2, 0, 0 })
    //                        }
    //                    },
    //                    { "XYZ", new ListEx<ControlInfo>
    //                        {
    //                            new ControlInfo( new Mesh( new OctahedronGeometry( 0.2, 0 ), matInvisible ), null, null )
    //                        }
    //                    },
    //                    { "XY", new ListEx<ControlInfo>
    //                        {
    //                            new ControlInfo( new Mesh( new BoxGeometry( 0.2, 0.2, 0.01 ), matInvisible ), new double[]{ 0.15, 0.15, 0 }, null)
    //                        }
    //                    },
    //                    { "YZ", new ListEx<ControlInfo>
    //                        {
    //                            new ControlInfo( new Mesh( new BoxGeometry( 0.2, 0.2, 0.01 ), matInvisible ), new double[]{ 0, 0.15, 0.15 }, new double[]{ 0, Math.PI / 2, 0 })
    //                        }
    //                    },
    //                    { "XZ", new ListEx<ControlInfo>
    //                        {
    //                            new ControlInfo( new Mesh( new BoxGeometry( 0.2, 0.2, 0.01 ), matInvisible ), new double[]{ 0.15, 0, 0.15 }, new double[]{ - Math.PI / 2, 0, 0 })
    //                        }
    //                    }
    //                };

    //            var helperTranslate = new JsObj<string, ListEx<ControlInfo>>
    //                {
    //                    { "START", new ListEx<ControlInfo>
    //                        {
    //                            new ControlInfo( new Mesh(new OctahedronGeometry(0.01, 2), matHelper), null, null, null, "helper" )
    //                        }
    //                    },
    //                    { "END", new ListEx<ControlInfo>
    //                        {
    //                            new ControlInfo( new Mesh(new OctahedronGeometry(0.01, 2), matHelper), null, null, null, "helper" )
    //                        }
    //                    },
    //                    { "DELTA", new ListEx<ControlInfo>
    //                        {
    //                            new ControlInfo(new Line(TranslateHelperGeometry(), matHelper), null, null, null, "helper" )
    //                        }
    //                    },
    //                    { "X", new ListEx<ControlInfo>
    //                        {
    //                            new ControlInfo( new Line(lineGeometry, matHelper.clone()), new double[]{ -1e3, 0, 0 }, null, new double[]{ 1e6, 1, 1 }, "helper" )
    //                        }
    //                    },
    //                    { "Y", new ListEx<ControlInfo>
    //                        {
    //                            new ControlInfo( new Line(lineGeometry, matHelper.clone()), new double[]{ 0, -1e3, 0 }, new double[]{ 0, 0, Math.PI / 2 }, new double[] { 1e6, 1, 1 }, "helper" )
    //                        }
    //                    },
    //                    { "Z", new ListEx<ControlInfo>
    //                        {
    //                            new ControlInfo( new Line(lineGeometry, matHelper.clone()), new double[]{ 0, 0, -1e3 }, new double[]{ 0, -Math.PI / 2, 0 }, new double[]{ 1e6, 1, 1 }, "helper" )
    //                        }
    //                    }
    //                };

    //            var gizmoRotate = new JsObj<string, ListEx<ControlInfo>>
    //                {
    //                    { "XYZE", new ListEx<ControlInfo>
    //                        {
    //                            new ControlInfo( new Mesh(CircleGeometry(0.5, 1), matGray), null, new double[] { 0, Math.PI / 2, 0 })
    //                        }
    //                    },
    //                    { "X", new ListEx<ControlInfo>
    //                        {
    //                            new ControlInfo( new Mesh(CircleGeometry(0.5, 0.5), matRed) )
    //                        }
    //                    },
    //                    { "Y", new ListEx<ControlInfo>
    //                        {
    //                            new ControlInfo( new Mesh(CircleGeometry(0.5, 0.5), matGreen), null, new double[] { 0, 0, - Math.PI / 2 })
    //                        }
    //                    },
    //                    { "Z", new ListEx<ControlInfo>
    //                        {
    //                            new ControlInfo( new Mesh(CircleGeometry(0.5, 0.5), matBlue), null, new double[] { 0, Math.PI / 2, 0 })
    //                        }
    //                    },
    //                    { "E", new ListEx<ControlInfo>
    //                        {
    //                            new ControlInfo( new Mesh(CircleGeometry(0.75, 1), matYellowTransparent), null, new double[] { 0, Math.PI / 2, 0 })
    //                        }
    //                    }
    //                };

    //            var helperRotate = new JsObj<string, ListEx<ControlInfo>>
    //                {
    //                    { "AXIS", new ListEx<ControlInfo>
    //                        {
    //                            new ControlInfo( new Line(lineGeometry, matHelper.clone()), new double[]{ - 1e3, 0, 0 }, null, new double[] { 1e6, 1, 1 }, "helper" )
    //                        }
    //                    }
    //                };

    //            var pickerRotate = new JsObj<string, ListEx<ControlInfo>>
    //                {
    //                    { "XYZE", new ListEx<ControlInfo>
    //                        {
    //                            new ControlInfo(new Mesh(new SphereGeometry(0.25, 10, 8), matInvisible) )
    //                        }
    //                    },
    //                    { "X", new ListEx<ControlInfo>
    //                        {
    //                            new ControlInfo( new Mesh(new TorusGeometry(0.5, 0.1, 4, 24), matInvisible), new double[]{ 0, 0, 0 }, new double[]{ 0, - Math.PI / 2, - Math.PI / 2 })
    //                        }
    //                    },
    //                    { "Y", new ListEx<ControlInfo>
    //                        {
    //                            new ControlInfo( new Mesh(new TorusGeometry(0.5, 0.1, 4, 24), matInvisible), new double[]{ 0, 0, 0 }, new double[]{Math.PI / 2, 0, 0 })
    //                        }
    //                    },
    //                    { "Z", new ListEx<ControlInfo>
    //                        {
    //                            new ControlInfo(  new Mesh(new TorusGeometry(0.5, 0.1, 4, 24), matInvisible), new double[] { 0, 0, 0 }, new double[] { 0, 0, - Math.PI / 2 }),
    //                        }
    //                    },
    //                    { "E", new ListEx<ControlInfo>
    //                        {
    //                            new ControlInfo(  new Mesh(new TorusGeometry(0.75, 0.1, 2, 24), matInvisible) )
    //                        }
    //                    }
    //                };

    //            var gizmoScale = new JsObj<string, ListEx<ControlInfo>>
    //                {
    //                    { "X", new ListEx<ControlInfo>
    //                        {
    //                            new ControlInfo( new Mesh(scaleHandleGeometry, matRed), new double[]{ 0.5, 0, 0 }, new double[]{ 0, 0, - Math.PI / 2 }),
    //                            new ControlInfo( new Mesh(lineGeometry2, matRed), new double[]{ 0, 0, 0 }, new double[]{ 0, 0, - Math.PI / 2 }),
    //                            new ControlInfo( new Mesh(scaleHandleGeometry, matRed), new double[]{ - 0.5, 0, 0 }, new double[]{ 0, 0, Math.PI / 2 }),
    //                        }
    //                    },
    //                    { "Y", new ListEx<ControlInfo>
    //                        {
    //                            new ControlInfo( new Mesh(scaleHandleGeometry, matGreen), new double[]{ 0, 0.5, 0 }),
    //                            new ControlInfo( new Mesh(lineGeometry2, matGreen) ),
    //                            new ControlInfo( new Mesh(scaleHandleGeometry, matGreen), new double[]{ 0, - 0.5, 0 }, new double[]{ 0, 0, Math.PI}),
    //                        }
    //                    },
    //                    { "Z", new ListEx<ControlInfo>
    //                        {
    //                            new ControlInfo( new Mesh(scaleHandleGeometry, matBlue), new double[]{ 0, 0, 0.5 }, new double[]{Math.PI / 2, 0, 0 }),
    //                            new ControlInfo( new Mesh(lineGeometry2, matBlue), new double[]{ 0, 0, 0 }, new double[]{Math.PI / 2, 0, 0 }),
    //                            new ControlInfo( new Mesh(scaleHandleGeometry, matBlue), new double[]{ 0, 0, - 0.5 }, new double[]{ - Math.PI / 2, 0, 0 })
    //                        }
    //                    },
    //                    { "XY", new ListEx<ControlInfo>
    //                        {
    //                            new ControlInfo( new Mesh(new BoxGeometry(0.15, 0.15, 0.01), matBlueTransparent), new double[]{ 0.15, 0.15, 0 })
    //                        }
    //                    },
    //                    { "YZ", new ListEx<ControlInfo>
    //                        {
    //                            new ControlInfo( new Mesh(new BoxGeometry(0.15, 0.15, 0.01), matRedTransparent), new double[]{ 0, 0.15, 0.15 }, new double[]{ 0, Math.PI / 2, 0 })
    //                        }
    //                    },
    //                    { "XZ", new ListEx<ControlInfo>
    //                        {
    //                            new ControlInfo( new Mesh(new BoxGeometry(0.15, 0.15, 0.01), matGreenTransparent), new double[]{ 0.15, 0, 0.15 }, new double[]{ - Math.PI / 2, 0, 0 })
    //                        }
    //                    },
    //                    { "XYZ", new ListEx<ControlInfo>
    //                        {
    //                            new ControlInfo( new Mesh(new BoxGeometry(0.1, 0.1, 0.1), matWhiteTransparent.clone()) ),
    //                        }
    //                    }
    //                };

    //            var pickerScale = new JsObj<string, ListEx<ControlInfo>>
    //                {
    //                    {"X", new ListEx<ControlInfo>
    //                        {
    //                            new ControlInfo( new Mesh(new CylinderGeometry(0.2, 0, 0.6, 4), matInvisible), new double[]{ 0.3, 0, 0 }, new double[]{ 0, 0, - Math.PI / 2 }),
    //                            new ControlInfo(new Mesh(new CylinderGeometry(0.2, 0, 0.6, 4), matInvisible), new double[]{ -0.3, 0, 0 }, new double[]{ 0, 0, Math.PI / 2 })
    //                        }
    //                    },
    //                    {"Y", new ListEx<ControlInfo>
    //                        {
    //                            new ControlInfo( new Mesh(new CylinderGeometry(0.2, 0, 0.6, 4), matInvisible), new double[]{ 0, 0.3, 0 }),
    //                            new ControlInfo( new Mesh(new CylinderGeometry(0.2, 0, 0.6, 4), matInvisible), new double[]{ 0, - 0.3, 0 }, new double[]{ 0, 0, Math.PI})
    //                        }
    //                    },
    //                    {"Z", new ListEx<ControlInfo>
    //                        {
    //                            new ControlInfo( new Mesh(new CylinderGeometry(0.2, 0, 0.6, 4), matInvisible), new double[]{ 0, 0, 0.3 }, new double[]{Math.PI / 2, 0, 0 }),
    //                            new ControlInfo( new Mesh(new CylinderGeometry(0.2, 0, 0.6, 4), matInvisible), new double[]{ 0, 0, - 0.3 }, new double[]{ - Math.PI / 2, 0, 0 })
    //                        }
    //                    },

    //                    {"XY", new ListEx<ControlInfo>
    //                        {
    //                            new ControlInfo( new Mesh(new BoxGeometry(0.2, 0.2, 0.01), matInvisible), new double[]{ 0.15, 0.15, 0 }),
    //                        }
    //                    },

    //                    {"YZ", new ListEx<ControlInfo>
    //                        {
    //                            new ControlInfo( new Mesh(new BoxGeometry(0.2, 0.2, 0.01), matInvisible), new double[]{ 0, 0.15, 0.15 }, new double[]{ 0, Math.PI / 2, 0 }),
    //                        }
    //                    },

    //                    {"XZ", new ListEx<ControlInfo>
    //                        {
    //                            new ControlInfo( new Mesh(new BoxGeometry(0.2, 0.2, 0.01), matInvisible), new double[]{ 0.15, 0, 0.15 }, new double[]{ - Math.PI / 2, 0, 0 }),
    //                        }
    //                    },

    //                    {"XYZ", new ListEx<ControlInfo>
    //                        {
    //                            new ControlInfo( new Mesh(new BoxGeometry(0.2, 0.2, 0.2), matInvisible), new double[]{ 0, 0, 0 }),
    //                        }
    //                    }
    //                };

    //            var helperScale = new JsObj<string, ListEx<ControlInfo>>
    //                {
    //                    { "X", new ListEx<ControlInfo>
    //                        {
    //                            new ControlInfo( new Line(lineGeometry, matHelper.clone()), new double[] { - 1e3, 0, 0 }, null, new double[] { 1e6, 1, 1 }, "helper" )
    //                        }
    //                    },
    //                    { "Y", new ListEx<ControlInfo>
    //                        {
    //                            new ControlInfo( new Line(lineGeometry, matHelper.clone()), new double[] { 0, - 1e3, 0 }, new double[] { 0, 0, Math.PI / 2 }, new double[] { 1e6, 1, 1 }, "helper" )
    //                        }
    //                    },
    //                    { "Z", new ListEx<ControlInfo>
    //                        {
    //                            new ControlInfo( new Line(lineGeometry, matHelper.clone()), new double[] { 0, 0, - 1e3 }, new double[] { 0, - Math.PI / 2, 0 }, new double[] { 1e6, 1, 1 }, "helper" )
    //                        }
    //                    }
    //                };

    //            // Gizmo creation
    //            this.gizmo = new JsObj<string, Object3D>();
    //            this.picker = new JsObj<string, Object3D>();
    //            this.helper = new JsObj<string, Object3D>();

    //            this.add(this.gizmo["translate"] = setupGizmo(gizmoTranslate));
    //            this.add(this.gizmo["rotate"] = setupGizmo(gizmoRotate));
    //            this.add(this.gizmo["scale"] = setupGizmo(gizmoScale));
    //            this.add(this.picker["translate"] = setupGizmo(pickerTranslate));
    //            this.add(this.picker["rotate"] = setupGizmo(pickerRotate));
    //            this.add(this.picker["scale"] = setupGizmo(pickerScale));
    //            this.add(this.helper["translate"] = setupGizmo(helperTranslate));
    //            this.add(this.helper["rotate"] = setupGizmo(helperRotate));
    //            this.add(this.helper["scale"] = setupGizmo(helperScale));

    //            // Pickers should be hidden always

    //            this.picker["translate"].visible = false;
    //            this.picker["rotate"].visible = false;
    //            this.picker["scale"].visible = false;

    //        }

    //        /// <summary>
    //        /// Creates an Object3D with gizmos described in custom hierarchy definition.
    //        /// </summary>
    //        private Object3D setupGizmo(JsObj<string, ListEx<ControlInfo>> gizmoMap)
    //        {
    //            var gizmo = new Object3D();
    //            foreach (var name in gizmoMap.Keys)
    //            {
    //                for (int i = 0; i < gizmoMap[name].Length; i++)
    //                {
    //                    var obj = gizmoMap[name][i].obj3d.clone();
    //                    var position = gizmoMap[name][i].position;
    //                    var rotation = gizmoMap[name][i].rotation;
    //                    var scale = gizmoMap[name][i].scale;
    //                    var tag = gizmoMap[name][i].tag;

    //                    // name and tag properties are essential for picking and updating logic.
    //                    obj.name = name;
    //                    obj.userData["tag"] = tag;

    //                    if (position != null)
    //                    {
    //                        obj.position.Set(position[0], position[1], position[2]);
    //                    }

    //                    if (rotation != null)
    //                    {
    //                        obj.rotation.Set(rotation[0], rotation[1], rotation[2]);
    //                    }

    //                    if (scale != null)
    //                    {
    //                        obj.scale.Set(scale[0], scale[1], scale[2]);
    //                    }

    //                    obj.updateMatrix();

    //                    var tempGeometry = (obj as IGeometry).getGeometry().clone();
    //                    tempGeometry.applyMatrix4(obj.matrix);
    //                    (obj as IGeometry).setGeometry(tempGeometry);
    //                    obj.renderOrder = int.MaxValue;

    //                    obj.position.Set(0, 0, 0);
    //                    obj.rotation.Set(0, 0, 0);
    //                    obj.scale.Set(1, 1, 1);

    //                    gizmo.add(obj);
    //                }
    //            }
    //            return gizmo;
    //        }


    //        /// <summary>
    //        /// updateMatrixWorld will update transformations and appearance of individual handles
    //        /// </summary>
    //        public override void updateMatrixWorld(bool force = false)
    //        {
    //            var space = this.mode == "scale" ? "local" : this.space; // scale always oriented to local rotation

    //            var quaternion = space == "local" ? this.worldQuaternion : _identityQuaternion;

    //            // Show only gizmos for current transform mode

    //            this.gizmo["translate"].visible = this.mode == "translate";
    //            this.gizmo["rotate"].visible = this.mode == "rotate";
    //            this.gizmo["scale"].visible = this.mode == "scale";

    //            this.helper["translate"].visible = this.mode == "translate";
    //            this.helper["rotate"].visible = this.mode == "rotate";
    //            this.helper["scale"].visible = this.mode == "scale";


    //            var handles = new ListEx<Object3D>();
    //            handles = handles.Concat(this.picker[this.mode].children);
    //            handles = handles.Concat(this.gizmo[this.mode].children);
    //            handles = handles.Concat(this.helper[this.mode].children);

    //            for (int i = 0; i < handles.Length; i++)
    //            {
    //                var handle = handles[i];

    //                // hide aligned to camera
    //                handle.visible = true;
    //                handle.rotation.Set(0, 0, 0);
    //                handle.position.Copy(this.worldPosition);

    //                double factor = 0;

    //                if (this.camera is OrthographicCamera)
    //                {
    //                    var ortCamera = this.camera as OrthographicCamera;
    //                    factor = (ortCamera.top - ortCamera.bottom) / this.camera.zoom;
    //                }
    //                else
    //                {
    //                    var perCamera = this.camera as PerspectiveCamera;
    //                    factor = this.worldPosition.DistanceTo(this.cameraPosition) * Math.Min(1.9 * Math.Tan(Math.PI * perCamera.fov / 360) / perCamera.zoom, 7);
    //                }

    //                handle.scale.Set(1, 1, 1).MultiplyScalar(factor * this.size / 4.0);

    //                // TODO: simplify helpers and consider decoupling from gizmo

    //                if (handle.userData.TryGetValue("tag", out object val) && (string)val == "helper")
    //                {
    //                    handle.visible = false;

    //                    if (handle.name == "AXIS")
    //                    {
    //                        handle.visible = this.axis != null;

    //                        if (this.axis == "X")
    //                        {
    //                            _tempQuaternion.SetFromEuler(_tempEuler.Set(0, 0, 0));
    //                            handle.quaternion.Copy(quaternion).Multiply(_tempQuaternion);

    //                            if (Math.Abs(_alignVector.Copy(_unitX).ApplyQuaternion(quaternion).Dot(this.eye)) > 0.9)
    //                            {
    //                                handle.visible = false;
    //                            }
    //                        }

    //                        if (this.axis == "Y")
    //                        {
    //                            _tempQuaternion.SetFromEuler(_tempEuler.Set(0, 0, Math.PI / 2));
    //                            handle.quaternion.Copy(quaternion).Multiply(_tempQuaternion);

    //                            if (Math.Abs(_alignVector.Copy(_unitY).ApplyQuaternion(quaternion).Dot(this.eye)) > 0.9)
    //                            {
    //                                handle.visible = false;
    //                            }
    //                        }

    //                        if (this.axis == "Z")
    //                        {

    //                            _tempQuaternion.SetFromEuler(_tempEuler.Set(0, Math.PI / 2, 0));
    //                            handle.quaternion.Copy(quaternion).Multiply(_tempQuaternion);

    //                            if (Math.Abs(_alignVector.Copy(_unitZ).ApplyQuaternion(quaternion).Dot(this.eye)) > 0.9)
    //                            {
    //                                handle.visible = false;
    //                            }
    //                        }

    //                        if (this.axis == "XYZE")
    //                        {
    //                            _tempQuaternion.SetFromEuler(_tempEuler.Set(0, Math.PI / 2, 0));
    //                            _alignVector.Copy(this.rotationAxis);
    //                            handle.quaternion.SetFromRotationMatrix(_lookAtMatrix.LookAt(_zeroVector, _alignVector, _unitY));
    //                            handle.quaternion.Multiply(_tempQuaternion);
    //                            handle.visible = this.dragging;
    //                        }

    //                        if (this.axis == "E")
    //                        {
    //                            handle.visible = false;
    //                        }
    //                    }
    //                    else if (handle.name == "START")
    //                    {
    //                        handle.position.Copy(this.worldPositionStart);
    //                        handle.visible = this.dragging;
    //                    }
    //                    else if (handle.name == "END")
    //                    {
    //                        handle.position.Copy(this.worldPosition);
    //                        handle.visible = this.dragging;
    //                    }
    //                    else if (handle.name == "DELTA")
    //                    {
    //                        handle.position.Copy(this.worldPositionStart);
    //                        handle.quaternion.Copy(this.worldQuaternionStart);
    //                        _tempVector.Set(1e-10, 1e-10, 1e-10).Add(this.worldPositionStart).Sub(this.worldPosition).MultiplyScalar(-1);
    //                        _tempVector.ApplyQuaternion(this.worldQuaternionStart.Clone().Invert());
    //                        handle.scale.Copy(_tempVector);
    //                        handle.visible = this.dragging;
    //                    }
    //                    else
    //                    {
    //                        handle.quaternion.Copy(quaternion);
    //                        if (this.dragging)
    //                        {
    //                            handle.position.Copy(this.worldPositionStart);
    //                        }
    //                        else
    //                        {
    //                            handle.position.Copy(this.worldPosition);
    //                        }

    //                        if (this.axis != null)
    //                        {
    //                            handle.visible = this.axis.search(handle.name) != -1;
    //                        }
    //                    }

    //                    // If updating helper, skip rest of the loop
    //                    continue;
    //                }

    //                // Align handles to current local or world rotation

    //                handle.quaternion.Copy(quaternion);

    //                if (this.mode == "translate" || this.mode == "scale")
    //                {
    //                    // Hide translate and scale axis facing the camera

    //                    var AXIS_HIDE_THRESHOLD = 0.99;
    //                    var PLANE_HIDE_THRESHOLD = 0.2;

    //                    if (handle.name == "X")
    //                    {
    //                        if (Math.Abs(_alignVector.Copy(_unitX).ApplyQuaternion(quaternion).Dot(this.eye)) > AXIS_HIDE_THRESHOLD)
    //                        {
    //                            handle.scale.Set(1e-10, 1e-10, 1e-10);
    //                            handle.visible = false;
    //                        }
    //                    }

    //                    if (handle.name == "Y")
    //                    {
    //                        if (Math.Abs(_alignVector.Copy(_unitY).ApplyQuaternion(quaternion).Dot(this.eye)) > AXIS_HIDE_THRESHOLD)
    //                        {
    //                            handle.scale.Set(1e-10, 1e-10, 1e-10);
    //                            handle.visible = false;
    //                        }
    //                    }

    //                    if (handle.name == "Z")
    //                    {
    //                        if (Math.Abs(_alignVector.Copy(_unitZ).ApplyQuaternion(quaternion).Dot(this.eye)) > AXIS_HIDE_THRESHOLD)
    //                        {
    //                            handle.scale.Set(1e-10, 1e-10, 1e-10);
    //                            handle.visible = false;
    //                        }
    //                    }

    //                    if (handle.name == "XY")
    //                    {
    //                        if (Math.Abs(_alignVector.Copy(_unitZ).ApplyQuaternion(quaternion).Dot(this.eye)) < PLANE_HIDE_THRESHOLD)
    //                        {
    //                            handle.scale.Set(1e-10, 1e-10, 1e-10);
    //                            handle.visible = false;
    //                        }
    //                    }

    //                    if (handle.name == "YZ")
    //                    {
    //                        if (Math.Abs(_alignVector.Copy(_unitX).ApplyQuaternion(quaternion).Dot(this.eye)) < PLANE_HIDE_THRESHOLD)
    //                        {
    //                            handle.scale.Set(1e-10, 1e-10, 1e-10);
    //                            handle.visible = false;
    //                        }
    //                    }

    //                    if (handle.name == "XZ")
    //                    {
    //                        if (Math.Abs(_alignVector.Copy(_unitY).ApplyQuaternion(quaternion).Dot(this.eye)) < PLANE_HIDE_THRESHOLD)
    //                        {
    //                            handle.scale.Set(1e-10, 1e-10, 1e-10);
    //                            handle.visible = false;
    //                        }
    //                    }
    //                }
    //                else if (this.mode == "rotate")
    //                {
    //                    // Align handles to current local or world rotation

    //                    _tempQuaternion2.Copy(quaternion);
    //                    _alignVector.Copy(this.eye).ApplyQuaternion(_tempQuaternion.Copy(quaternion).Invert());

    //                    if (handle.name.search("E") != -1)
    //                    {
    //                        handle.quaternion.SetFromRotationMatrix(_lookAtMatrix.LookAt(this.eye, _zeroVector, _unitY));
    //                    }

    //                    if (handle.name == "X")
    //                    {
    //                        _tempQuaternion.SetFromAxisAngle(_unitX, Math.Atan2(-_alignVector.Y, _alignVector.Z));
    //                        _tempQuaternion.MultiplyQuaternions(_tempQuaternion2, _tempQuaternion);
    //                        handle.quaternion.Copy(_tempQuaternion);
    //                    }

    //                    if (handle.name == "Y")
    //                    {
    //                        _tempQuaternion.SetFromAxisAngle(_unitY, Math.Atan2(_alignVector.X, _alignVector.Z));
    //                        _tempQuaternion.MultiplyQuaternions(_tempQuaternion2, _tempQuaternion);
    //                        handle.quaternion.Copy(_tempQuaternion);
    //                    }

    //                    if (handle.name == "Z")
    //                    {
    //                        _tempQuaternion.SetFromAxisAngle(_unitZ, Math.Atan2(_alignVector.Y, _alignVector.X));
    //                        _tempQuaternion.MultiplyQuaternions(_tempQuaternion2, _tempQuaternion);
    //                        handle.quaternion.Copy(_tempQuaternion);
    //                    }
    //                }

    //                // Hide disabled axes
    //                handle.visible = handle.visible && (handle.name.indexOf("X") == -1 || this.showX);
    //                handle.visible = handle.visible && (handle.name.indexOf("Y") == -1 || this.showY);
    //                handle.visible = handle.visible && (handle.name.indexOf("Z") == -1 || this.showZ);
    //                handle.visible = handle.visible && (handle.name.indexOf("E") == -1 || (this.showX && this.showY && this.showZ));

    //                // highlight selected axis
    //                var matObj = (handle as IMaterialObject).getMaterial();
    //                matObj.userData["_color"] = matObj.userData["_color"] ?? matObj.color.Clone();
    //                matObj.userData["_opacity"] = matObj.userData["_opacity"] ?? matObj.opacity;

    //                matObj.color.Copy((Color)matObj.userData["_color"]);
    //                matObj.opacity = (double)matObj.userData["_opacity"];

    //                if (this.enabled && this.axis != null)
    //                {
    //                    if (handle.name == this.axis)
    //                    {
    //                        matObj.color.SetHex(0xffff00);
    //                        matObj.opacity = 1.0;
    //                    }
    //                    else if (this.axis.split("").Some((a) =>
    //                    {
    //                        return handle.name == a;
    //                    }))
    //                    {
    //                        matObj.color.SetHex(0xffff00);
    //                        matObj.opacity = 1.0;
    //                    }
    //                }
    //            }

    //            base.updateMatrixWorld(force);
    //        }
    //    }

    //    public class TransformControlsPlane : Mesh
    //    {
    //        public string space;
    //        public string mode;
    //        public string axis;
    //        public Vector3 worldPosition;
    //        public Vector3 eye;
    //        public Quaternion worldQuaternion;
    //        public Quaternion cameraQuaternion;

    //        //public Camera camera;
    //        //public Object3D obj;
    //        //public bool enabled;
    //        //public object translationSnap;
    //        //public object rotationSnap;
    //        //public object scaleSnap;
    //        //public double size;
    //        //public bool dragging;
    //        //public bool showX;
    //        //public bool showY;
    //        //public bool showZ;

    //        public TransformControlsPlane()
    //            : base(
    //                new PlaneGeometry(100000, 100000, 2, 2),
    //                new MeshBasicMaterial
    //                {
    //                    visible = false,
    //                    wireframe = true,
    //                    side = Constants.DoubleSide,
    //                    transparent = true,
    //                    opacity = 0.1,
    //                    toneMapped = false
    //                }
    //            )
    //        {
    //            this.type = "TransformControlsPlane";
    //        }
    //        public override void updateMatrixWorld(bool force = false)
    //        {
    //            var space = this.space;
    //            this.position.Copy(this.worldPosition);
    //            if (this.mode == "scale")
    //                space = "local"; // scale always oriented to local rotation

    //            _v1.Copy(_unitX).ApplyQuaternion(space == "local" ? this.worldQuaternion : _identityQuaternion);
    //            _v2.Copy(_unitY).ApplyQuaternion(space == "local" ? this.worldQuaternion : _identityQuaternion);
    //            _v3.Copy(_unitZ).ApplyQuaternion(space == "local" ? this.worldQuaternion : _identityQuaternion);

    //            // Align the plane for current transform mode, axis and space.

    //            _alignVector.Copy(_v2);

    //            switch (this.mode)
    //            {
    //                case "translate":
    //                case "scale":
    //                    switch (this.axis)
    //                    {
    //                        case "X":
    //                            _alignVector.Copy(this.eye).Cross(_v1);
    //                            _dirVector.Copy(_v1).Cross(_alignVector);
    //                            break;
    //                        case "Y":
    //                            _alignVector.Copy(this.eye).Cross(_v2);
    //                            _dirVector.Copy(_v2).Cross(_alignVector);
    //                            break;
    //                        case "Z":
    //                            _alignVector.Copy(this.eye).Cross(_v3);
    //                            _dirVector.Copy(_v3).Cross(_alignVector);
    //                            break;
    //                        case "XY":
    //                            _dirVector.Copy(_v3);
    //                            break;
    //                        case "YZ":
    //                            _dirVector.Copy(_v1);
    //                            break;
    //                        case "XZ":
    //                            _alignVector.Copy(_v3);
    //                            _dirVector.Copy(_v2);
    //                            break;
    //                        case "XYZ":
    //                        case "E":
    //                            _dirVector.Set(0, 0, 0);
    //                            break;
    //                    }
    //                    break;
    //                case "rotate":
    //                default:
    //                    // special case for rotate
    //                    _dirVector.Set(0, 0, 0);
    //                    break;
    //            }

    //            if (_dirVector.Length() == 0)
    //            {
    //                // If in rotate mode, make the plane parallel to camera
    //                this.quaternion.Copy(this.cameraQuaternion);
    //            }
    //            else
    //            {
    //                _tempMatrix.LookAt(_tempVector.Set(0, 0, 0), _dirVector, _alignVector);
    //                this.quaternion.SetFromRotationMatrix(_tempMatrix);
    //            }
    //            base.updateMatrixWorld(force);

    //        }
    //    }

    //    private static Raycaster _raycaster = new Raycaster();
    //    private static Vector3 _tempVector = new Vector3();
    //    private static Vector3 _tempVector2 = new Vector3();
    //    private static Quaternion _tempQuaternion = new Quaternion();
    //    private static Unit _unit = new Unit()
    //    {
    //        X = new Vector3(1, 0, 0),
    //        Y = new Vector3(0, 1, 0),
    //        Z = new Vector3(0, 0, 1)
    //    };

    //    private static EventArgs _changeEvent = new EventArgs { type = "change" };
    //    private static EventArgs _mouseDownEvent = new EventArgs { type = "mouseDown" };
    //    private static EventArgs _mouseUpEvent = new EventArgs { type = "mouseUp", mode = null };
    //    private static EventArgs _objectChangeEvent = new EventArgs { type = "objectChange" };

    //    public GLControl glControl;
    //    public TransformControlsGizmo _gizmo;
    //    public TransformControlsPlane _plane;

    //    public Vector3 _offset;
    //    public Vector3 _startNorm;
    //    public Vector3 _endNorm;
    //    public Vector3 _cameraScale;

    //    public Vector3 _parentPosition;
    //    public Quaternion _parentQuaternion;
    //    public Quaternion _parentQuaternionInv;
    //    public Vector3 _parentScale;

    //    public Vector3 _worldScaleStart;
    //    public Quaternion _worldQuaternionInv;
    //    public Vector3 _worldScale;

    //    public Vector3 _positionStart;
    //    public Quaternion _quaternionStart;
    //    public Vector3 _scaleStart;

    //    #region Props
    //    private Camera _cameraDefault;
    //    private Camera _camera;
    //    public Camera camera
    //    {
    //        get => _camera ?? _cameraDefault;
    //        set
    //        {
    //            if (value != _camera)
    //            {
    //                _camera = value;
    //                //this._plane.camera = value;
    //                this._gizmo.camera = value;
    //                this.dispatchEvent(new EventArgs { type = nameof(camera) + "-changed", value = value });
    //                this.dispatchEvent(_changeEvent);
    //            }
    //        }
    //    }

    //    private Object3D _objDefault;
    //    private Object3D _obj;
    //    public Object3D obj
    //    {
    //        get => _obj ?? _objDefault;
    //        set
    //        {
    //            if (value != _obj)
    //            {
    //                _obj = value;
    //                //this._plane.obj = value;
    //                //this._gizmo.obj = value;
    //                this.dispatchEvent(new EventArgs { type = nameof(obj) + "-changed", value = value });
    //                this.dispatchEvent(_changeEvent);
    //            }
    //        }
    //    }

    //    private bool _enabledDefault;
    //    private bool? _enabled;
    //    public bool enabled
    //    {
    //        get => _enabled ?? _enabledDefault;
    //        set
    //        {
    //            if (value != _enabled)
    //            {
    //                _enabled = value;
    //                //this._plane.enabled = value;
    //                this._gizmo.enabled = value;

    //                this.dispatchEvent(new EventArgs { type = nameof(enabled) + "-changed", value = value });
    //                this.dispatchEvent(_changeEvent);
    //            }
    //        }
    //    }

    //    private string _axisDefault;
    //    private string _axis;
    //    public string axis
    //    {
    //        get => _axis ?? _axisDefault;
    //        set
    //        {
    //            if (value != _axis)
    //            {
    //                _axis = value;
    //                this._plane.axis = value;
    //                this._gizmo.axis = value;
    //                this.dispatchEvent(new EventArgs { type = nameof(axis) + "-changed", value = value });
    //                this.dispatchEvent(_changeEvent);
    //            }
    //        }
    //    }

    //    private string _modeDefault;
    //    private string _mode;
    //    public string mode
    //    {
    //        get => _mode ?? _modeDefault;
    //        set
    //        {
    //            if (value != _mode)
    //            {
    //                _mode = value;
    //                this._plane.mode = value;
    //                this._gizmo.mode = value;
    //                this.dispatchEvent(new EventArgs { type = nameof(mode) + "-changed", value = value });
    //                this.dispatchEvent(_changeEvent);
    //            }
    //        }
    //    }

    //    private double? _translationSnapDefault;
    //    private double? _translationSnap;
    //    public double? translationSnap
    //    {
    //        get => _translationSnap ?? _translationSnapDefault;
    //        set
    //        {
    //            if (value != _translationSnap)
    //            {
    //                _translationSnap = value;
    //                //this._plane.translationSnap = value;
    //                //this._gizmo.translationSnap = value;
    //                this.dispatchEvent(new EventArgs { type = nameof(translationSnap) + "-changed", value = value });
    //                this.dispatchEvent(_changeEvent);
    //            }
    //        }
    //    }

    //    private double? _rotationSnapDefault;
    //    private double? _rotationSnap;
    //    public double? rotationSnap
    //    {
    //        get => _rotationSnap ?? _rotationSnapDefault;
    //        set
    //        {
    //            if (value != _rotationSnap)
    //            {
    //                _rotationSnap = value;
    //                //this._plane.rotationSnap = value;
    //                //this._gizmo.rotationSnap = value;
    //                this.dispatchEvent(new EventArgs { type = nameof(rotationSnap) + "-changed", value = value });
    //                this.dispatchEvent(_changeEvent);
    //            }
    //        }
    //    }

    //    private double? _scaleSnapDefault;
    //    private double? _scaleSnap;
    //    public double? scaleSnap
    //    {
    //        get => _scaleSnap ?? _scaleSnapDefault;
    //        set
    //        {
    //            if (value != _scaleSnap)
    //            {
    //                _scaleSnap = value;
    //                //this._plane.scaleSnap = value;
    //                //this._gizmo.scaleSnap = value;
    //                this.dispatchEvent(new EventArgs { type = nameof(scaleSnap) + "-changed", value = value });
    //                this.dispatchEvent(_changeEvent);
    //            }
    //        }
    //    }

    //    private string _spaceDefault;
    //    private string _space;
    //    public string space
    //    {
    //        get => _space ?? _spaceDefault;
    //        set
    //        {
    //            if (value != _space)
    //            {
    //                _space = value;
    //                this._plane.space = value;
    //                this._gizmo.space = value;
    //                this.dispatchEvent(new EventArgs { type = nameof(space) + "-changed", value = value });
    //                this.dispatchEvent(_changeEvent);
    //            }
    //        }
    //    }

    //    private double _sizeDefault;
    //    private double? _size;
    //    public double size
    //    {
    //        get => _size ?? _sizeDefault;
    //        set
    //        {
    //            if (value != _size)
    //            {
    //                _size = value;
    //                //this._plane.size = value;
    //                this._gizmo.size = value;
    //                this.dispatchEvent(new EventArgs { type = nameof(size) + "-changed", value = value });
    //                this.dispatchEvent(_changeEvent);
    //            }
    //        }
    //    }

    //    private bool _draggingDefault;
    //    private bool? _dragging;
    //    public bool dragging
    //    {
    //        get => _dragging ?? _draggingDefault;
    //        set
    //        {
    //            if (value != _dragging)
    //            {
    //                _dragging = value;
    //                //this._plane.dragging = value;
    //                this._gizmo.dragging = value;
    //                this.dispatchEvent(new EventArgs { type = nameof(dragging) + "-changed", value = value });
    //                this.dispatchEvent(_changeEvent);
    //            }
    //        }
    //    }

    //    private bool _showXDefault;
    //    private bool? _showX;
    //    public bool showX
    //    {
    //        get => _showX ?? _showXDefault;
    //        set
    //        {
    //            if (value != _showX)
    //            {
    //                _showX = value;
    //                //this._plane.showX = value;
    //                this._gizmo.showX = value;
    //                this.dispatchEvent(new EventArgs { type = nameof(showX) + "-changed", value = value });
    //                this.dispatchEvent(_changeEvent);
    //            }
    //        }
    //    }

    //    private bool _showYDefault;
    //    private bool? _showY;
    //    public bool showY
    //    {
    //        get => _showY ?? _showYDefault;
    //        set
    //        {
    //            if (value != _showY)
    //            {
    //                _showY = value;
    //                //this._plane.showY = value;
    //                this._gizmo.showY = value;
    //                this.dispatchEvent(new EventArgs { type = nameof(showY) + "-changed", value = value });
    //                this.dispatchEvent(_changeEvent);
    //            }
    //        }
    //    }

    //    private bool _showZDefault;
    //    private bool? _showZ;
    //    public bool showZ
    //    {
    //        get => _showZ ?? _showZDefault;
    //        set
    //        {
    //            if (value != _showZ)
    //            {
    //                _showZ = value;
    //                //this._plane.showZ = value;
    //                this._gizmo.showZ = value;
    //                this.dispatchEvent(new EventArgs { type = nameof(showZ) + "-changed", value = value });
    //                this.dispatchEvent(_changeEvent);
    //            }
    //        }
    //    }

    //    private Vector3 _worldPositionDefault;
    //    private Vector3 _worldPosition;
    //    public Vector3 worldPosition
    //    {
    //        get => _worldPosition ?? _worldPositionDefault;
    //        set
    //        {
    //            if (value != _worldPosition)
    //            {
    //                _worldPosition = value;
    //                this._plane.worldPosition = value;
    //                this._gizmo.worldPosition = value;
    //                this.dispatchEvent(new EventArgs { type = nameof(worldPosition) + "-changed", value = value });
    //                this.dispatchEvent(_changeEvent);
    //            }
    //        }
    //    }

    //    private Vector3 _worldPositionStartDefault;
    //    private Vector3 _worldPositionStart;
    //    public Vector3 worldPositionStart
    //    {
    //        get => _worldPositionStart ?? _worldPositionStartDefault;
    //        set
    //        {
    //            if (value != _worldPositionStart)
    //            {
    //                _worldPositionStart = value;
    //                //this._plane.worldPositionStart = value;
    //                this._gizmo.worldPositionStart = value;
    //                this.dispatchEvent(new EventArgs { type = nameof(worldPositionStart) + "-changed", value = value });
    //                this.dispatchEvent(_changeEvent);
    //            }
    //        }
    //    }

    //    private Quaternion _worldQuaternionDefault;
    //    private Quaternion _worldQuaternion;
    //    public Quaternion worldQuaternion
    //    {
    //        get => _worldQuaternion ?? _worldQuaternionDefault;
    //        set
    //        {
    //            if (value != _worldQuaternion)
    //            {
    //                _worldQuaternion = value;
    //                this._plane.worldQuaternion = value;
    //                this._gizmo.worldQuaternion = value;
    //                this.dispatchEvent(new EventArgs { type = nameof(worldQuaternion) + "-changed", value = value });
    //                this.dispatchEvent(_changeEvent);
    //            }
    //        }
    //    }

    //    private Quaternion _worldQuaternionStartDefault;
    //    private Quaternion _worldQuaternionStart;
    //    public Quaternion worldQuaternionStart
    //    {
    //        get => _worldQuaternionStart ?? _worldQuaternionStartDefault;
    //        set
    //        {
    //            if (value != _worldQuaternionStart)
    //            {
    //                _worldQuaternionStart = value;
    //                //this._plane.worldQuaternionStart = value;
    //                this._gizmo.worldQuaternionStart = value;
    //                this.dispatchEvent(new EventArgs { type = nameof(worldQuaternionStart) + "-changed", value = value });
    //                this.dispatchEvent(_changeEvent);
    //            }
    //        }
    //    }

    //    private Vector3 _cameraPositionDefault;
    //    private Vector3 _cameraPosition;
    //    public Vector3 cameraPosition
    //    {
    //        get => _cameraPosition ?? _cameraPositionDefault;
    //        set
    //        {
    //            if (value != _cameraPosition)
    //            {
    //                _cameraPosition = value;
    //                //this._plane.cameraPosition = value;
    //                this._gizmo.cameraPosition = value;
    //                this.dispatchEvent(new EventArgs { type = nameof(cameraPosition) + "-changed", value = value });
    //                this.dispatchEvent(_changeEvent);
    //            }
    //        }
    //    }

    //    private Quaternion _cameraQuaternionDefault;
    //    private Quaternion _cameraQuaternion;
    //    public Quaternion cameraQuaternion
    //    {
    //        get => _cameraQuaternion ?? _cameraQuaternionDefault;
    //        set
    //        {
    //            if (value != _cameraQuaternion)
    //            {
    //                _cameraQuaternion = value;
    //                this._plane.cameraQuaternion = value;
    //                //this._gizmo.cameraQuaternion = value;
    //                this.dispatchEvent(new EventArgs { type = nameof(cameraQuaternion) + "-changed", value = value });
    //                this.dispatchEvent(_changeEvent);
    //            }
    //        }
    //    }

    //    private Vector3 _pointStartDefault;
    //    private Vector3 _pointStart;
    //    public Vector3 pointStart
    //    {
    //        get => _pointStart ?? _pointStartDefault;
    //        set
    //        {
    //            if (value != _pointStart)
    //            {
    //                _pointStart = value;
    //                //this._plane.pointStart = value;
    //                //this._gizmo.pointStart = value;
    //                this.dispatchEvent(new EventArgs { type = nameof(pointStart) + "-changed", value = value });
    //                this.dispatchEvent(_changeEvent);
    //            }
    //        }
    //    }

    //    private Vector3 _pointEndDefault;
    //    private Vector3 _pointEnd;
    //    public Vector3 pointEnd
    //    {
    //        get => _pointEnd ?? _pointEndDefault;
    //        set
    //        {
    //            if (value != _pointEnd)
    //            {
    //                _pointEnd = value;
    //                //this._plane.pointEnd = value;
    //                //this._gizmo.pointEnd = value;
    //                this.dispatchEvent(new EventArgs { type = nameof(pointEnd) + "-changed", value = value });
    //                this.dispatchEvent(_changeEvent);
    //            }
    //        }
    //    }

    //    private Vector3 _rotationAxisDefault;
    //    private Vector3 _rotationAxis;
    //    public Vector3 rotationAxis
    //    {
    //        get => _rotationAxis ?? _rotationAxisDefault;
    //        set
    //        {
    //            if (value != _rotationAxis)
    //            {
    //                _rotationAxis = value;
    //                //this._plane.rotationAxis = value;
    //                this._gizmo.rotationAxis = value;
    //                this.dispatchEvent(new EventArgs { type = nameof(rotationAxis) + "-changed", value = value });
    //                this.dispatchEvent(_changeEvent);
    //            }
    //        }
    //    }

    //    private double _rotationAngleDefault;
    //    private double? _rotationAngle;
    //    public double rotationAngle
    //    {
    //        get => _rotationAngle ?? _rotationAngleDefault;
    //        set
    //        {
    //            if (value != _rotationAngle)
    //            {
    //                _rotationAngle = value;
    //                //this._plane.rotationAngle = value;
    //                //this._gizmo.rotationAngle = value;
    //                this.dispatchEvent(new EventArgs { type = nameof(rotationAngle) + "-changed", value = value });
    //                this.dispatchEvent(_changeEvent);
    //            }
    //        }
    //    }

    //    private Vector3 _eyeDefault;
    //    private Vector3 _eye;
    //    public Vector3 eye
    //    {
    //        get => _eye ?? _eyeDefault;
    //        set
    //        {
    //            if (value != _eye)
    //            {
    //                _eye = value;
    //                this._plane.eye = value;
    //                this._gizmo.eye = value;
    //                this.dispatchEvent(new EventArgs { type = nameof(eye) + "-changed", value = value });
    //                this.dispatchEvent(_changeEvent);
    //            }
    //        }
    //    }
    //    #endregion Props
    //    public TransformControls(Camera camera, GLControl domElement)
    //    {
    //        if (domElement == null)
    //        {
    //            console.warn("THREE.TransformControls: The second parameter \"domElement\" is now mandatory.");
    //            //domElement = document;
    //        }

    //        this.visible = false;
    //        this.glControl = domElement;
    //        //this.domElement.style.touchAction = 'none'; // disable touch scroll

    //        var _gizmo = new TransformControlsGizmo();
    //        this._gizmo = _gizmo;
    //        this.add(_gizmo);

    //        var _plane = new TransformControlsPlane();
    //        this._plane = _plane;
    //        this.add(_plane);

    //        // Define properties with getters/setter
    //        // Setting the defined property will automatically trigger change event
    //        // Defined properties are passed down to gizmo and plane

    //        defineProperty("camera", camera);
    //        defineProperty("object", null);
    //        defineProperty("enabled", true);
    //        defineProperty("axis", null);
    //        defineProperty("mode", "translate");
    //        defineProperty("translationSnap", null);
    //        defineProperty("rotationSnap", null);
    //        defineProperty("scaleSnap", null);
    //        defineProperty("space", "world");
    //        defineProperty("size", 1);
    //        defineProperty("dragging", false);
    //        defineProperty("showX", true);
    //        defineProperty("showY", true);
    //        defineProperty("showZ", true);

    //        // Reusable utility variables

    //        var worldPosition = new Vector3();
    //        var worldPositionStart = new Vector3();
    //        var worldQuaternion = new Quaternion();
    //        var worldQuaternionStart = new Quaternion();
    //        var cameraPosition = new Vector3();
    //        var cameraQuaternion = new Quaternion();
    //        var pointStart = new Vector3();
    //        var pointEnd = new Vector3();
    //        var rotationAxis = new Vector3();
    //        var rotationAngle = 0;
    //        var eye = new Vector3();

    //        // TODO: remove properties unused in plane and gizmo

    //        defineProperty("worldPosition", worldPosition);
    //        defineProperty("worldPositionStart", worldPositionStart);
    //        defineProperty("worldQuaternion", worldQuaternion);
    //        defineProperty("worldQuaternionStart", worldQuaternionStart);
    //        defineProperty("cameraPosition", cameraPosition);
    //        defineProperty("cameraQuaternion", cameraQuaternion);
    //        defineProperty("pointStart", pointStart);
    //        defineProperty("pointEnd", pointEnd);
    //        defineProperty("rotationAxis", rotationAxis);
    //        defineProperty("rotationAngle", rotationAngle);
    //        defineProperty("eye", eye);

    //        this._offset = new Vector3();
    //        this._startNorm = new Vector3();
    //        this._endNorm = new Vector3();
    //        this._cameraScale = new Vector3();

    //        this._parentPosition = new Vector3();
    //        this._parentQuaternion = new Quaternion();
    //        this._parentQuaternionInv = new Quaternion();
    //        this._parentScale = new Vector3();

    //        this._worldScaleStart = new Vector3();
    //        this._worldQuaternionInv = new Quaternion();
    //        this._worldScale = new Vector3();

    //        this._positionStart = new Vector3();
    //        this._quaternionStart = new Quaternion();
    //        this._scaleStart = new Vector3();

    //        //this._getPointer = getPointer.bind(this);
    //        //this._onPointerDown = onPointerDown.bind(this);
    //        //this._onPointerHover = onPointerHover.bind(this);
    //        //this._onPointerMove = onPointerMove.bind(this);
    //        //this._onPointerUp = onPointerUp.bind(this);

    //        this.glControl.MouseDown += this.onPointerDown;
    //        this.glControl.MouseMove += this.onPointerHover;
    //        this.glControl.MouseUp += this.onPointerUp;
    //    }

    //    /// <summary>
    //    /// Defined getter, setter and store for a property
    //    /// </summary>
    //    public void defineProperty(string propName, object defaultValue)
    //    {
    //        var propValue = defaultValue;
    //        switch (propName)
    //        {
    //            case "camera":
    //                this._cameraDefault = propValue as Camera;
    //                this.camera = this._cameraDefault;
    //                break;
    //            case "object":
    //                this._objDefault = propValue as Object3D;
    //                this.obj = _objDefault;
    //                break;
    //            case "enabled":
    //                this._enabledDefault = Convert.ToBoolean(propValue);
    //                this.enabled = _enabledDefault;
    //                break;
    //            case "axis":
    //                this._axisDefault = (string)propValue;
    //                this.axis = _axisDefault;
    //                break;
    //            case "mode":
    //                this._modeDefault = (string)propValue;
    //                this.mode = _modeDefault;
    //                break;
    //            case "translationSnap":
    //                if (propValue != null)
    //                {
    //                    this._translationSnapDefault = Convert.ToDouble(propValue);
    //                    this.translationSnap = _translationSnapDefault;
    //                }
    //                break;
    //            case "rotationSnap":
    //                if (propValue != null)
    //                {
    //                    this._rotationSnapDefault = Convert.ToDouble(propValue);
    //                    this.rotationSnap = _rotationSnapDefault;
    //                }
    //                break;
    //            case "scaleSnap":
    //                if (propValue != null)
    //                {
    //                    this._scaleSnapDefault = Convert.ToDouble(propValue);
    //                    this.scaleSnap = _scaleSnapDefault;
    //                }
    //                break;
    //            case "space":
    //                this._spaceDefault = (string)propValue;
    //                this.space = _spaceDefault;
    //                break;
    //            case "size":
    //                this._sizeDefault = Convert.ToDouble(propValue);
    //                this.size = _sizeDefault;
    //                break;
    //            case "dragging":
    //                this._draggingDefault = Convert.ToBoolean(propValue);
    //                this.dragging = _draggingDefault;
    //                break;
    //            case "showX":
    //                this._showXDefault = Convert.ToBoolean(propValue);
    //                this.showX = _showXDefault;
    //                break;
    //            case "showY":
    //                this._showYDefault = Convert.ToBoolean(propValue);
    //                this.showY = _showXDefault;
    //                break;
    //            case "showZ":
    //                this._showZDefault = Convert.ToBoolean(propValue);
    //                this.showZ = _showZDefault;
    //                break;
    //            case "worldPosition":
    //                this._worldPositionDefault = propValue as Vector3;
    //                this.worldPosition = _worldPositionDefault;
    //                break;
    //            case "worldPositionStart":
    //                this._worldPositionStartDefault = propValue as Vector3;
    //                this.worldPositionStart = _worldPositionStartDefault;
    //                break;
    //            case "worldQuaternion":
    //                this._worldQuaternionDefault = propValue as Quaternion;
    //                this.worldQuaternion = _worldQuaternionDefault;
    //                break;
    //            case "worldQuaternionStart":
    //                this._worldQuaternionStartDefault = propValue as Quaternion;
    //                this.worldQuaternionStart = _worldQuaternionStartDefault;
    //                break;
    //            case "cameraPosition":
    //                this._cameraPositionDefault = propValue as Vector3;
    //                this.cameraPosition = _cameraPositionDefault;
    //                break;
    //            case "cameraQuaternion":
    //                this._cameraQuaternionDefault = propValue as Quaternion;
    //                this.cameraQuaternion = _cameraQuaternionDefault;
    //                break;
    //            case "pointStart":
    //                this._pointStartDefault = propValue as Vector3;
    //                this.pointStart = _pointStartDefault;
    //                break;
    //            case "pointEnd":
    //                this._pointEndDefault = propValue as Vector3;
    //                this.pointEnd = _pointEndDefault;
    //                break;
    //            case "rotationAxis":
    //                this._rotationAxisDefault = propValue as Vector3;
    //                this.rotationAxis = _rotationAxisDefault;
    //                break;
    //            case "rotationAngle":
    //                this._rotationAngleDefault = Convert.ToDouble(propValue);
    //                this.rotationAngle = _rotationAngleDefault;
    //                break;
    //            case "eye":
    //                this._eyeDefault = propValue as Vector3;
    //                this.eye = _eyeDefault;
    //                break;
    //            default:
    //                break;
    //        }
    //    }

    //    public override void updateMatrixWorld(bool force = false)
    //    {
    //        if (this.obj != null)
    //        {
    //            this.obj.updateMatrixWorld();

    //            if (this.obj.parent == null)
    //            {
    //                console.error("TransformControls: The attached 3D object must be a part of the scene graph.");
    //            }
    //            else
    //            {
    //                this.obj.parent.matrixWorld.decompose(this._parentPosition, this._parentQuaternion, this._parentScale);
    //            }

    //            this.obj.matrixWorld.decompose(this.worldPosition, this.worldQuaternion, this._worldScale);

    //            this._parentQuaternionInv.Copy(this._parentQuaternion).Invert();
    //            this._worldQuaternionInv.Copy(this.worldQuaternion).Invert();
    //        }

    //        this.camera.updateMatrixWorld();
    //        this.camera.matrixWorld.decompose(this.cameraPosition, this.cameraQuaternion, this._cameraScale);

    //        if (this.camera is OrthographicCamera)
    //        {
    //            this.camera.getWorldDirection(this.eye).Negate();
    //        }
    //        else
    //        {
    //            this.eye.Copy(this.cameraPosition).Sub(this.worldPosition).Normalize();
    //        }
    //        base.updateMatrixWorld(force);
    //    }

    //    private void pointerHover(Pointer pointer)
    //    {
    //        if (this.obj == null || this.dragging)
    //            return;

    //        _raycaster.setFromCamera(pointer.ToV2(), this.camera);

    //        if (intersectObjectWithRay(this._gizmo.picker[this.mode], _raycaster, out Raycaster.Intersection intersect))
    //        {
    //            this.axis = intersect.target.name;
    //        }
    //        else
    //        {
    //            this.axis = null;
    //        }
    //    }

    //    private void pointerDown(Pointer pointer)
    //    {
    //        if (this.obj == null || this.dragging == true || pointer.button != MouseButtons.Left)
    //            return;

    //        if (this.axis != null)
    //        {
    //            _raycaster.setFromCamera(pointer.ToV2(), this.camera);

    //            if (intersectObjectWithRay(this._plane, _raycaster, out Raycaster.Intersection planeIntersect, true))
    //            {
    //                this.obj.updateMatrixWorld();
    //                this.obj.parent.updateMatrixWorld();

    //                this._positionStart.Copy(this.obj.position);
    //                this._quaternionStart.Copy(this.obj.quaternion);
    //                this._scaleStart.Copy(this.obj.scale);

    //                this.obj.matrixWorld.decompose(this.worldPositionStart, this.worldQuaternionStart, this._worldScaleStart);

    //                this.pointStart.Copy(planeIntersect.point).Sub(this.worldPositionStart);
    //            }

    //            this.dragging = true;
    //            _mouseDownEvent.mode = this.mode;
    //            this.dispatchEvent(_mouseDownEvent);
    //        }
    //    }

    //    private void pointerMove(Pointer pointer)
    //    {
    //        var axis = this.axis;
    //        var mode = this.mode;
    //        var obj = this.obj;
    //        var space = this.space;

    //        if (mode == "scale")
    //        {
    //            space = "local";
    //        }
    //        else if (axis == "E" || axis == "XYZE" || axis == "XYZ")
    //        {
    //            space = "world";
    //        }

    //        if (obj == null || axis == null || this.dragging == false)// || pointer.button != -1)
    //            return;

    //        _raycaster.setFromCamera(pointer.ToV2(), this.camera);

    //        if (!intersectObjectWithRay(this._plane, _raycaster, out Raycaster.Intersection planeIntersect, true))
    //            return;

    //        this.pointEnd.Copy(planeIntersect.point).Sub(this.worldPositionStart);

    //        if (mode == "translate")
    //        {
    //            // Apply translate

    //            this._offset.Copy(this.pointEnd).Sub(this.pointStart);

    //            if (space == "local" && axis != "XYZ")
    //            {
    //                this._offset.ApplyQuaternion(this._worldQuaternionInv);
    //            }

    //            if (axis.indexOf("X") == -1) this._offset.X = 0;
    //            if (axis.indexOf("Y") == -1) this._offset.Y = 0;
    //            if (axis.indexOf("Z") == -1) this._offset.Z = 0;

    //            if (space == "local" && axis != "XYZ")
    //            {
    //                this._offset.ApplyQuaternion(this._quaternionStart).Divide(this._parentScale);
    //            }
    //            else
    //            {
    //                this._offset.ApplyQuaternion(this._parentQuaternionInv).Divide(this._parentScale);
    //            }

    //            obj.position.Copy(this._offset).Add(this._positionStart);

    //            // Apply translation snap

    //            if (this.translationSnap != null)
    //            {
    //                var translationSnap = (double)this.translationSnap;
    //                if (space == "local")
    //                {
    //                    obj.position.ApplyQuaternion(_tempQuaternion.Copy(this._quaternionStart).Invert());

    //                    if (axis.search("X") != -1)
    //                    {
    //                        obj.position.X = Math.Round(obj.position.X / translationSnap) * translationSnap;
    //                    }

    //                    if (axis.search("Y") != -1)
    //                    {
    //                        obj.position.Y = Math.Round(obj.position.Y / translationSnap) * translationSnap;
    //                    }

    //                    if (axis.search("Z") != -1)
    //                    {
    //                        obj.position.Z = Math.Round(obj.position.Z / translationSnap) * translationSnap;
    //                    }

    //                    obj.position.ApplyQuaternion(this._quaternionStart);

    //                }

    //                if (space == "world")
    //                {
    //                    if (obj.parent != null)
    //                    {
    //                        obj.position.Add(_tempVector.SetFromMatrixPosition(obj.parent.matrixWorld));
    //                    }

    //                    if (axis.search("X") != -1)
    //                    {
    //                        obj.position.X = Math.Round(obj.position.X / translationSnap) * translationSnap;
    //                    }

    //                    if (axis.search("Y") != -1)
    //                    {
    //                        obj.position.Y = Math.Round(obj.position.Y / translationSnap) * translationSnap;
    //                    }

    //                    if (axis.search("Z") != -1)
    //                    {
    //                        obj.position.Z = Math.Round(obj.position.Z / translationSnap) * translationSnap;
    //                    }

    //                    if (obj.parent != null)
    //                    {
    //                        obj.position.Sub(_tempVector.SetFromMatrixPosition(obj.parent.matrixWorld));
    //                    }
    //                }
    //            }
    //        }
    //        else if (mode == "scale")
    //        {

    //            if (axis.search("XYZ") != -1)
    //            {
    //                var d = this.pointEnd.Length() / this.pointStart.Length();

    //                if (this.pointEnd.Dot(this.pointStart) < 0) d *= -1;

    //                _tempVector2.Set(d, d, d);
    //            }
    //            else
    //            {

    //                _tempVector.Copy(this.pointStart);
    //                _tempVector2.Copy(this.pointEnd);

    //                _tempVector.ApplyQuaternion(this._worldQuaternionInv);
    //                _tempVector2.ApplyQuaternion(this._worldQuaternionInv);

    //                _tempVector2.Divide(_tempVector);

    //                if (axis.search("X") == -1)
    //                {
    //                    _tempVector2.X = 1;
    //                }

    //                if (axis.search("Y") == -1)
    //                {
    //                    _tempVector2.Y = 1;
    //                }

    //                if (axis.search("Z") == -1)
    //                {
    //                    _tempVector2.Z = 1;
    //                }
    //            }

    //            // Apply scale

    //            obj.scale.Copy(this._scaleStart).Multiply(_tempVector2);

    //            if (this.scaleSnap != null)
    //            {
    //                var scaleSnap = (double)this.scaleSnap;
    //                if (axis.search("X") != -1)
    //                {
    //                    obj.scale.X = Math.Round(obj.scale.X / scaleSnap) * scaleSnap;// || scaleSnap;
    //                }

    //                if (axis.search("Y") != -1)
    //                {
    //                    obj.scale.Y = Math.Round(obj.scale.Y / scaleSnap) * scaleSnap;// || scaleSnap;
    //                }

    //                if (axis.search("Z") != -1)
    //                {
    //                    obj.scale.Z = Math.Round(obj.scale.Z / scaleSnap) * scaleSnap;// || scaleSnap;
    //                }
    //            }
    //        }
    //        else if (mode == "rotate")
    //        {
    //            this._offset.Copy(this.pointEnd).Sub(this.pointStart);

    //            var ROTATION_SPEED = 20 / this.worldPosition.DistanceTo(_tempVector.SetFromMatrixPosition(this.camera.matrixWorld));

    //            if (axis == "E")
    //            {
    //                this.rotationAxis.Copy(this.eye);
    //                this.rotationAngle = this.pointEnd.AngleTo(this.pointStart);

    //                this._startNorm.Copy(this.pointStart).Normalize();
    //                this._endNorm.Copy(this.pointEnd).Normalize();

    //                this.rotationAngle *= (this._endNorm.Cross(this._startNorm).Dot(this.eye) < 0 ? 1 : -1);
    //            }
    //            else if (axis == "XYZE")
    //            {
    //                this.rotationAxis.Copy(this._offset).Cross(this.eye).Normalize();
    //                this.rotationAngle = this._offset.Dot(_tempVector.Copy(this.rotationAxis).Cross(this.eye)) * ROTATION_SPEED;
    //            }
    //            else if (axis == "X" || axis == "Y" || axis == "Z")
    //            {
    //                this.rotationAxis.Copy(_unit[axis]);

    //                _tempVector.Copy(_unit[axis]);

    //                if (space == "local")
    //                {

    //                    _tempVector.ApplyQuaternion(this.worldQuaternion);

    //                }

    //                this.rotationAngle = this._offset.Dot(_tempVector.Cross(this.eye).Normalize()) * ROTATION_SPEED;

    //            }

    //            // Apply rotation snap

    //            if (this.rotationSnap != null)
    //            {
    //                var rotationSnap = (double)this.rotationSnap;
    //                this.rotationAngle = Math.Round(this.rotationAngle / rotationSnap) * rotationSnap;
    //            }

    //            // Apply rotate
    //            if (space == "local" && axis != "E" && axis != "XYZE")
    //            {

    //                obj.quaternion.Copy(this._quaternionStart);
    //                obj.quaternion.Multiply(_tempQuaternion.SetFromAxisAngle(this.rotationAxis, this.rotationAngle)).Normalize();

    //            }
    //            else
    //            {

    //                this.rotationAxis.ApplyQuaternion(this._parentQuaternionInv);
    //                obj.quaternion.Copy(_tempQuaternion.SetFromAxisAngle(this.rotationAxis, this.rotationAngle));
    //                obj.quaternion.Multiply(this._quaternionStart).Normalize();

    //            }

    //        }

    //        this.dispatchEvent(_changeEvent);
    //        this.dispatchEvent(_objectChangeEvent);

    //    }

    //    private void pointerUp(Pointer pointer)
    //    {
    //        if (pointer.button != MouseButtons.Left)
    //            return;

    //        if (this.dragging && (this.axis != null))
    //        {
    //            _mouseUpEvent.mode = this.mode;
    //            this.dispatchEvent(_mouseUpEvent);
    //        }

    //        this.dragging = false;
    //        this.axis = null;
    //    }

    //    public void dispose()
    //    {
    //        this.glControl.MouseDown -= this.onPointerDown;
    //        this.glControl.MouseMove -= this.onPointerHover;
    //        this.glControl.MouseMove -= this.onPointerMove;
    //        this.glControl.MouseMove -= this.onPointerUp;

    //        this.traverse((child) =>
    //        {
    //            if (child is ISolid)
    //            {
    //                (child as ISolid).getGeometry().dispose();
    //                (child as ISolid).getMaterial().dispose();
    //            }
    //        });
    //    }


    //    // Set current object
    //    public TransformControls attach(Object3D obj)
    //    {
    //        this.obj = obj;
    //        this.visible = true;

    //        return this;
    //    }

    //    // Detach from object
    //    public TransformControls detach()
    //    {
    //        this.obj = null;
    //        this.visible = false;
    //        this.axis = null;

    //        return this;
    //    }

    //    public void reset()
    //    {
    //        if (!this.enabled)
    //            return;

    //        if (this.dragging)
    //        {
    //            this.obj.position.Copy(this._positionStart);
    //            this.obj.quaternion.Copy(this._quaternionStart);
    //            this.obj.scale.Copy(this._scaleStart);

    //            this.dispatchEvent(_changeEvent);
    //            this.dispatchEvent(_objectChangeEvent);

    //            this.pointStart.Copy(this.pointEnd);
    //        }
    //    }

    //    public Raycaster getRaycaster()
    //    {
    //        return _raycaster;
    //    }

    //    // TODO: deprecate

    //    public string getMode()
    //    {
    //        return this.mode;
    //    }

    //    public void setMode(string mode)
    //    {
    //        this.mode = mode;
    //    }

    //    public void setTranslationSnap(double translationSnap)
    //    {
    //        this.translationSnap = translationSnap;
    //    }

    //    public void setRotationSnap(double rotationSnap)
    //    {
    //        this.rotationSnap = rotationSnap;
    //    }

    //    public void setScaleSnap(double scaleSnap)
    //    {
    //        this.scaleSnap = scaleSnap;
    //    }

    //    public void setSize(double size)
    //    {
    //        this.size = size;
    //    }

    //    public void setSpace(string space)
    //    {
    //        this.space = space;
    //    }


    //    // mouse / touch event handlers
    //    private Pointer getPointer(System.Windows.Forms.MouseEventArgs args)
    //    {
    //        var rect = this.glControl;

    //        return new Pointer()
    //        {
    //            x = (args.X - rect.Left) / (double)rect.Width * 2 - 1,
    //            y = -(args.Y - rect.Top) / (double)rect.Height * 2 + 1,
    //            button = args.Button
    //        };
    //    }

    //    private void onPointerHover(object sender, MouseEventArgs args)
    //    {
    //        if (!this.enabled) return;

    //        this.pointerHover(this.getPointer(args));
    //    }
    //    private void onPointerDown(object sender, MouseEventArgs args)
    //    {
    //        if (!this.enabled) return;

    //        this.glControl.MouseMove += this.onPointerMove;

    //        this.pointerHover(this.getPointer(args));
    //        this.pointerDown(this.getPointer(args));
    //    }
    //    private void onPointerMove(object sender, MouseEventArgs args)
    //    {
    //        if (!this.enabled) return;

    //        this.pointerMove(this.getPointer(args));
    //    }

    //    private void onPointerUp(object sender, MouseEventArgs args)
    //    {
    //        if (!this.enabled) return;

    //        this.glControl.MouseMove -= this.onPointerMove;

    //        this.pointerUp(this.getPointer(args));
    //    }

    //    private bool intersectObjectWithRay(Object3D obj, Raycaster raycaster, out Raycaster.Intersection intersection, bool includeInvisible = false)
    //    {
    //        intersection = null;
    //        var allIntersections = raycaster.intersectObject(obj, true);

    //        for (int i = 0; i < allIntersections.Length; i++)
    //        {
    //            if (allIntersections[i].target.visible || includeInvisible)
    //            {
    //                intersection = allIntersections[i];
    //                return true;
    //            }
    //        }
    //        return false;
    //    }
    //}
}